/*
 * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import camera from '@ohos.multimedia.camera';
import deviceInfo from '@ohos.deviceInfo';
import fs from '@ohos.file.fs';
import PhotoAccessHelper from '@ohos.file.photoAccessHelper';
import fileIo from '@ohos.fileio';
import image from '@ohos.multimedia.image';
import media from '@ohos.multimedia.media';
import mediaLibrary from '@ohos.multimedia.mediaLibrary';
import Logger from '../utlis/Logger';
import MediaModel from './MediaModel';
import MediaUtils from './MediaUtils';
import Hilog from '../Utils/HiLogUtils'
import util from '@ohos.util';

const FOUR = 4; // format
const EIGHT = 8; // capacity
const FOUR_THOUSAND_AND_SIXTY_NINE = 4096; // buffer大小


const cameraMode = {
  modePhoto: 0, // 拍照模式
  modeVideo: 1, // 录像模式
};

const cameraWH = {
  width: 480,
  height: 360,
};

/**
 * 分辨率
 */
export enum VideoFrame {
  VIDEOFRAME_1920_1080,
  VIDEOFRAME_1280_720,
  VIDEOFRAME_800_600,
};

type VideoFrameWH = {
  width: number;
  height: number;
};

const TAG = '[CameraModel]';

export default class CameraService {
  private mediaUtil: MediaUtils = undefined;
  private phAccessHelper: PhotoAccessHelper.PhotoAccessHelper = undefined;

  private mediaModel: MediaModel = undefined;
  private fileAsset: mediaLibrary.FileAsset = undefined;
  private cameraMgr: camera.CameraManager = undefined;
  private camerasArray: Array<camera.CameraDevice> = undefined;
  private cameraInput: camera.CameraInput = undefined;
  private previewOutput: camera.PreviewOutput = undefined;
  private photoOutPut: camera.PhotoOutput = undefined;
  private capSession: camera.CaptureSession = undefined;
  private videoOutput: camera.VideoOutput = undefined;
  private capability: camera.CameraOutputCapability = undefined;

  private avRecorder: media.AVRecorder = undefined;
  private receiver: image.ImageReceiver = undefined;
  private photoPath: string = '';
  private fd: number = -1;
  private takePictureHandle: (photoUri: string) => void = undefined;
  private currentMode:number = cameraMode.modePhoto;

  public Base64String:string = "";

  private videoFrameWH: VideoFrameWH = {
    width: 480,
    height: 360,
  }; // 视频分辨率
  private imageRotation: camera.ImageRotation = camera.ImageRotation.ROTATION_0; // 图片旋转角度

  // private videoProfile: media.VideoRecorderProfile = {
  private videoProfile: media.AVRecorderProfile = {
    audioChannels: 2,
    audioCodec: media.CodecMimeType.AUDIO_AAC,
    audioBitrate: 48000,
    audioSampleRate: 48000,
    fileFormat: media.ContainerFormatType.CFT_MPEG_4,
    videoBitrate: 48000,
    videoCodec: media.CodecMimeType.VIDEO_MPEG4,
    videoFrameWidth: 480,
    videoFrameHeight: 360,
    videoFrameRate: 30,
  };
  private videoSourceType: number = 0;

  // constructor() {
    constructor(context: any) {
    this.mediaUtil = MediaUtils.getInstance(context);
    this.mediaModel = MediaModel.getMediaInstance();
    this.phAccessHelper = PhotoAccessHelper.getPhotoAccessHelper(context);

    this.receiver = image.createImageReceiver(
      cameraWH.width,
      cameraWH.height,
      FOUR,
      EIGHT
    );
    Hilog.info( 'createImageReceiver');
    this.receiver.on('imageArrival', () => {
      Hilog.info( 'imageArrival');
      this.receiver.readNextImage((err, image) => {
        Hilog.info( 'readNextImage');
        if (err || image === undefined) {
          Hilog.error( 'failed to get valid image');
          return;
        }
        image.getComponent(FOUR, (errMsg, img) => {
          Hilog.info( 'getComponent');
          if (errMsg || img === undefined) {
            Hilog.info( 'failed to get valid buffer');
            return;
          }
          let buffer = new ArrayBuffer(FOUR_THOUSAND_AND_SIXTY_NINE);
          if (img.byteBuffer) {
            buffer = img.byteBuffer;
          } else {
            Hilog.error( 'img.byteBuffer is undefined');
          }
          // this.saveImage(buffer, image);
          this.savePicture(buffer, image);
          this.ConvertBase64(buffer);
        });
      });
    });
  }

  /**
   * 设置分辨率
   * @param videoFrame
   */
  setVideoFrameWH(videoFrame: VideoFrame): void {
    switch (videoFrame) {
      case VideoFrame.VIDEOFRAME_800_600:
        this.videoFrameWH = {
          width: 800,
          height: 600,
        };
        Hilog.info(
          `setVideoFrameWH videoFrameWH:${JSON.stringify(this.videoFrameWH)}`
        );
        break;
      case VideoFrame.VIDEOFRAME_1280_720:
        this.videoFrameWH = {
          width: 1280,
          height: 720,
        };
        Hilog.info(
          `setVideoFrameWH videoFrameWH:${JSON.stringify(this.videoFrameWH)}`
        );
        break;
      case VideoFrame.VIDEOFRAME_1920_1080:
        this.videoFrameWH = {
          width: 1920,
          height: 1080,
        };
        Hilog.info(
          `setVideoFrameWH videoFrameWH:${JSON.stringify(this.videoFrameWH)}`
        );
        break;
    }
  }
  /**
   * 设置图片旋转角度
   * @param imageRotation
   */
  setImageRotation(imageRotation: camera.ImageRotation): void {
    this.imageRotation = imageRotation;
  }

  /**
   * 保存图片
   * @param buffer
   * @param img
   */
  async saveImage(buffer: ArrayBuffer, img: image.Image): Promise<void> {
    Hilog.info( 'savePicture');
    this.fileAsset = await this.mediaModel.createAndGetUri(mediaLibrary.MediaType.IMAGE);
    this.photoPath = this.fileAsset.uri;
    Hilog.info( `this.photoUri = ${this.photoPath}`);
    this.fd = await this.mediaModel.getFdPath(this.fileAsset);
    Hilog.info( `this.fd = ${this.fd}`);
    await fileIo.write(this.fd, buffer);
    await this.fileAsset.close(this.fd);
    await img.release();
    Hilog.info( 'save image done');
    if (this.takePictureHandle) {
      this.takePictureHandle(this.photoPath);
    }
  }

  async savePicture(buffer: ArrayBuffer, img: image.Image) {
    Hilog.info( 'savePicture');
    // this.fileAsset = await this.mediaModel.createAndGetUri(mediaLibrary.MediaType.IMAGE);
    // this.fileAsset = await this.mediaUtil.createAndGetUri(mediaLibrary.MediaType.IMAGE);

    // let photoAccessHelper = PhotoAccessHelper.getPhotoAccessHelper(context);
    // let testFileName:String = 'testFile' + Date.now() + '.jpg';
    // let photoAsset = await photoAccessHelper.createAsset(testFileName);

    // let displayName: string = 'testPhoto' + Date.now() + '.jpg';
    // let createOption: PhotoAccessHelper.p = {
    //   subtype: PhotoAccessHelper.PhotoSubtype.DEFAULT
    // };
    // let photoAsset: PhotoAccessHelper.PhotoAsset = await this.phAccessHelper.createAsset(displayName, createOption);

    let photoType: PhotoAccessHelper.PhotoType = PhotoAccessHelper.PhotoType.IMAGE;
    let extension:string = 'jpg';
    let options: PhotoAccessHelper.CreateOptions = {
      title: 'testPhoto'
    }
    let uri = await this.phAccessHelper.createAsset(photoType, extension, options);

    // const fd = await fs.open('rw');
    // fs.write(fd, buffer);
    // await photoAsset.close(fd);
    // img.release();

    let file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    await fs.write(file.fd, buffer);
    await fs.close(file.fd);
    this.photoPath = uri;


    // this.photoPath = this.fileAsset.uri;
    // Hilog.info( `this.photoUri = ${this.photoPath}`);
    // this.fd = await this.mediaModel.getFdPath(this.fileAsset);
    // Hilog.info( `this.fd = ${this.fd}`);
    // await fileIo.write(this.fd, buffer);
    // await this.fileAsset.close(this.fd);
    // await img.release();
    Hilog.info( 'save image done');
    if (this.takePictureHandle) {
      Hilog.info( `handleTakePicture====================== ${this.photoPath}`);
      this.takePictureHandle(this.photoPath);
      Hilog.info( `handleTakePicture=====================1= ${this.photoPath}`);
    }
  }


  // async function example() {
  //   console.info('createAssetDemo');
  //   let photoType: photoAccessHelper.PhotoType = photoAccessHelper.PhotoType.IMAGE;
  //   let extension:string = 'jpg';
  //   let options: photoAccessHelper.CreateOptions = {
  //     title: 'testPhoto'
  //   }
  //   phAccessHelper.createAsset(photoType, extension, options, (err, uri) => {
  //     if (uri !== undefined) {
  //       console.info('createAsset uri' + uri);
  //       console.info('createAsset successfully');
  //     } else {
  //       Hilog.error(`createAsset failed, error: ${err.code}, ${err.message}`);
  //     }
  //   });
  // }

  /**
   * 初始化相机
   * @param surfaceId
   */
  async initCamera(surfaceId: string, cameraId: string): Promise<void> {
    Hilog.info( `initCamera surfaceId:${surfaceId}`);
    await this.cameraRelease();
    Hilog.info( `deviceInfo.deviceType = ${deviceInfo.deviceType}`);
    if (deviceInfo.deviceType === 'default') {
      Hilog.info( `deviceInfo.deviceType default 1 = ${deviceInfo.deviceType}`);
      this.videoSourceType = 1;
      Hilog.info( `deviceInfo.deviceType default 2 = ${deviceInfo.deviceType}`);
    } else {
      Hilog.info( `deviceInfo.deviceType other 1 = ${deviceInfo.deviceType}`);
      this.videoSourceType = 0;
      Hilog.info( `deviceInfo.deviceType other 2 = ${deviceInfo.deviceType}`);
    }
    Hilog.info( 'getCameraManager begin');
    try {
      Hilog.info( 'getCameraManager try begin');
      this.cameraMgr = camera.getCameraManager(globalThis.cameraContext);
      Hilog.info( 'getCameraManager try end');
    } catch (e) {
      Hilog.info( `getCameraManager catch e:${JSON.stringify(e)}`);
      Hilog.info( `getCameraManager catch code:${JSON.stringify(e.code)}`);
      Hilog.info( `getCameraManager catch message:${JSON.stringify(e.message)}`);
    }
    Hilog.info( 'getCameraManager end');
    Hilog.info( `getCameraManager ${JSON.stringify(this.cameraMgr)}`);
    this.camerasArray = this.cameraMgr.getSupportedCameras();
    Hilog.info( `get cameras ${this.camerasArray.length}`);
    if (this.camerasArray.length === 0) {
      Hilog.info( 'cannot get cameras');
      return;
    }

    let mCamera = this.camerasArray[cameraId];
    this.cameraInput = this.cameraMgr.createCameraInput(mCamera);
    this.cameraInput.open();
    Hilog.info( 'createCameraInput');
    this.capability = this.cameraMgr.getSupportedOutputCapability(mCamera);
    let previewProfile = this.capability.previewProfiles[0];
    this.previewOutput = this.cameraMgr.createPreviewOutput(
      previewProfile,
      surfaceId
    );
    Hilog.info( 'createPreviewOutput');
    let rSurfaceId = await this.receiver.getReceivingSurfaceId();
    let photoProfile = this.capability.photoProfiles[0];
    this.photoOutPut = this.cameraMgr.createPhotoOutput(
      photoProfile,
      rSurfaceId
    );
    // this.capSession = this.cameraMgr.createCaptureSession();
    this.capSession = await this.cameraMgr.createCaptureSession();
    Hilog.info( 'createCaptureSession');
    // this.capSession.beginConfig();
    await this.capSession.beginConfig();
    Hilog.info( 'beginConfig');
    // this.capSession.addInput(this.cameraInput);
    // this.capSession.addOutput(this.previewOutput);
    // this.capSession.addOutput(this.photoOutPut);
    await this.capSession.addInput(this.cameraInput);
    await this.capSession.addOutput(this.previewOutput);
    await this.capSession.addOutput(this.photoOutPut);
    await this.capSession.commitConfig();
    await this.capSession.start();
    Hilog.info( 'captureSession start');
  }

  setTakePictureHandleCallback(callback): void {
    this.takePictureHandle = callback;
  }

  /**
   * 拍照
   */
  async takePicture(): Promise<void> {
    Hilog.info( 'takePicture');
    if (this.currentMode === cameraMode.modeVideo) {
      this.currentMode = cameraMode.modePhoto;
    }
    Hilog.info( `takePicture imageRotation:${this.imageRotation}`);
    let photoSettings = {
      rotation: this.imageRotation,
      quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM,
      // location: {
      //   // 位置信息，经纬度
      //   latitude: 12.9698,
      //   longitude: 77.75,
      //   altitude: 1000,
      // },
      mirror: true,
    };
    await this.photoOutPut.capture(photoSettings);
    Hilog.info( 'takePicture done');
    AppStorage.Set('isRefresh', true);
  }

  /**
   * 开始录像
   */
  async startVideo(): Promise<void> {
    Hilog.info( 'startVideo begin');
    Hilog.info( 'startVideo 1');
    await this.capSession.stop();
    Hilog.info( 'startVideo 2');
    this.capSession.beginConfig();
    Hilog.info( 'startVideo 3');
    if (this.currentMode === cameraMode.modePhoto) {
      this.currentMode = cameraMode.modeVideo;
      if (this.photoOutPut) {
        this.capSession.removeOutput(this.photoOutPut);
        this.photoOutPut.release();
      }
    } else {
      if (this.videoOutput) {
        try {
          Hilog.info( 'startVideo 4');
          this.capSession.removeOutput(this.videoOutput);
          Hilog.info( 'startVideo 5');
        } catch (e) {
          Hilog.info( `startVideo catch e:${JSON.stringify(e)}`);
          Hilog.info( `startVideo catch code:${JSON.stringify(e.code)}`);
          Hilog.info( `startVideo catch message:${JSON.stringify(e.message)}`);
        }
      }
    }
    if (this.videoOutput) {
      try {
        Hilog.info( 'startVideo 6');
        this.capSession.removeOutput(this.videoOutput);
        Hilog.info( 'startVideo 7');
      } catch (e) {
        Hilog.info( `startVideo catch e:${JSON.stringify(e)}`);
        Hilog.info( `startVideo catch code:${JSON.stringify(e.code)}`);
        Hilog.info( `startVideo catch message:${JSON.stringify(e.message)}`);
      }
      try {
        Hilog.info( 'startVideo release 1');
        await this.videoOutput.release();
        Hilog.info( 'startVideo release 2');
      } catch (e) {
        Hilog.info( `startVideo catch e:${JSON.stringify(e)}`);
        Hilog.info( `startVideo catch code:${JSON.stringify(e.code)}`);
        Hilog.info( `startVideo catch message:${JSON.stringify(e.message)}`);
      }
    }
    Hilog.info( 'startVideo 8');
    this.fileAsset = await this.mediaModel.createAndGetUri(mediaLibrary.MediaType.VIDEO);
    Hilog.info( `startVideo fileAsset:${this.fileAsset}`);
    this.fd = await this.mediaModel.getFdPath(this.fileAsset);
    Hilog.info( `startVideo fd:${this.fd}`);
    media.createAVRecorder(async (error, recorder) => {
      Hilog.info( `startVideo into createAVRecorder:${recorder}`);
      if (recorder != null) {
        Hilog.info( `startVideo into recorder:${recorder}`);
        this.avRecorder = recorder;
        Hilog.info( `startVideo createAVRecorder success:${this.avRecorder}`);
        // 当前录像配置
        let currConfig = {
          audioSourceType: 1,
          videoSourceType: this.videoSourceType,
          profile: this.videoProfile,
          url: `fd://${this.fd}`,
          rotation: 0
        };
        Hilog.info( `startVideo into recorder:${recorder}`);
        await this.avRecorder.prepare(currConfig);
        Hilog.info( `startVideo videoConfig:${JSON.stringify(currConfig)}`);
        let videoId = await this.avRecorder.getInputSurface();
        let videoProfile = this.capability.videoProfiles[0];
        Hilog.info( `startVideo capability.videoProfiles[]=: ${JSON.stringify(this.capability.videoProfiles)}`);
        Hilog.info( `startVideo videoProfile:${JSON.stringify(videoProfile)}`);
        this.videoOutput = this.cameraMgr.createVideoOutput(videoProfile, videoId);
        Hilog.info( `startVideo videoOutput:${this.videoOutput}`);
        this.capSession.addOutput(this.videoOutput);
        Hilog.info( 'startVideo addOutput');
        await this.capSession.commitConfig();
        Hilog.info( 'startVideo commitConfig');
        await this.capSession.start();
        Hilog.info( 'startVideo commitConfig capSession');
        await this.videoOutput.start();
        Hilog.info( 'startVideo commitConfig videoOutput');
        try {
          Hilog.info( 'startVideo avRecorder.start 1');
          await this.avRecorder.start();
          Hilog.info( 'startVideo avRecorder.start 2');
        } catch (e) {
          Hilog.info( `startVideo catch e:${JSON.stringify(e)}`);
        }

        Hilog.info( 'startVideo end');

      } else {
        Hilog.info( `startVideo createAVRecorder fail, error:${error}`);
      }
    });
  }

  /**
   * 停止录像
   */
  async stopVideo(): Promise<void> {
    Hilog.info( 'stopVideo called');
    await this.avRecorder.stop();
    await this.avRecorder.release();
    await this.videoOutput.stop();
    await this.fileAsset.close(this.fd);
  }

  /**
   * 资源释放
   */
  async cameraRelease(): Promise<void> {
    Hilog.info( 'releaseCamera');
    if (this.cameraInput) {
      await this.cameraInput.close();
    }
    if (this.previewOutput) {
      await this.previewOutput.release();
    }
    if (this.photoOutPut) {
      await this.photoOutPut.release();
    }
    if (this.videoOutput) {
      await this.videoOutput.release();
    }
    if (this.capSession) {
      await this.capSession.release();
    }
  }

  async ConvertBase64(buffer: ArrayBuffer): Promise<void> {
    let base64 = new util.Base64Helper(); // 实例化Base64Helper
    //let num = fs.readSync(fd, buf);   // 读取文件
    let data = base64.encodeSync(new Uint8Array(buffer.slice(0, buffer.byteLength))) //  转换成Uint8Array
    console.info(`data长度:${data.length}`)
    console.info(`data:${data}`)
    let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM : true })
    let retStr = textDecoder.decodeWithStream( data , {stream: false});
    console.info(`data长度:${data.length}`)
    console.info(`data:${data}`)
    this.Base64String = retStr;
    //this.UploadBase64(retStr);
  }

  /**
   * 返回Base64String
   */
  public reBase64() : string
  {
    return this.Base64String;
  }
}
